/*
 * Copyright 2018-2024 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.ktorm.ksp.compiler.generator

import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.symbol.ClassKind
import com.squareup.kotlinpoet.AnnotationSpec
import com.squareup.kotlinpoet.FileSpec
import org.ktorm.ksp.spi.ExtCodeGenerator
import org.ktorm.ksp.spi.TableMetadata
import java.util.*

internal object FileGenerator {
    val extCodeGenerators = ServiceLoader.load(ExtCodeGenerator::class.java, javaClass.classLoader).toList()

    fun generate(table: TableMetadata, environment: SymbolProcessorEnvironment): FileSpec {
        val fileName = table.entityClass.packageName.asString().replace('.', '/') + "/" + table.tableClassName + ".kt"
        environment.logger.info("[ktorm-ksp-compiler] generate file: $fileName")

        val fileSpec = FileSpec.builder(table.entityClass.packageName.asString(), table.tableClassName)
            .addFileComment("Auto-generated by ktorm-ksp-compiler, DO NOT EDIT!")
            .addAnnotation(
                AnnotationSpec.builder(Suppress::class).addMember("%S", "RedundantVisibilityModifier").build())
            .addType(TableClassGenerator.generate(table, environment))
            .addProperty(EntitySequencePropertyGenerator.generate(table))

        if (table.entityClass.classKind == ClassKind.INTERFACE) {
            if (table.columns.any { it.isReference }) {
                fileSpec.addProperty(RefsPropertyGenerator.generate(table))
                fileSpec.addType(RefsClassGenerator.generate(table))
            }

            fileSpec.addFunction(PseudoConstructorFunctionGenerator.generate(table))
            fileSpec.addFunction(CopyFunctionGenerator.generate(table))

            for (func in ComponentFunctionGenerator.generate(table)) {
                fileSpec.addFunction(func)
            }
        } else {
            fileSpec.addFunction(AddFunctionGenerator.generate(table))

            if (table.columns.any { it.isPrimaryKey }) {
                fileSpec.addFunction(UpdateFunctionGenerator.generate(table))
            }
        }

        for (generator in extCodeGenerators) {
            val desc = "Code generated by ext generator: $generator"

            for (type in generator.generateTypes(table, environment)) {
                fileSpec.addType(
                    type.toBuilder().addKdoc(if (type.kdoc.isEmpty()) desc else "\n\n$desc").build()
                )
            }

            for (property in generator.generateProperties(table, environment)) {
                fileSpec.addProperty(
                    property.toBuilder().addKdoc(if (property.kdoc.isEmpty()) desc else "\n\n$desc").build()
                )
            }

            for (function in generator.generateFunctions(table, environment)) {
                fileSpec.addFunction(
                    function.toBuilder().addKdoc(if (function.kdoc.isEmpty()) desc else "\n\n$desc").build()
                )
            }
        }

        return fileSpec.build()
    }
}
